/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License");  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * http//www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
 * the specific language governing rights and limitations under the License.
 *
 * The Original Code is OWLArtAPI_Sesame2Impl.
 *
 * The Initial Developer of the Original Code is University of Roma Tor Vergata.
 * Portions created by University of Roma Tor Vergata are Copyright (C) 2009.
 * All Rights Reserved.
 *
 * OWLArtAPI_Sesame2Impl was developed by the Artificial Intelligence Research Group
 * (ai-nlp.info.uniroma2.it) at the University of Roma Tor Vergata
 * Current information about OWLArtAPI_Sesame2Impl can be obtained at 
 * http//ai-nlp.info.uniroma2.it/software/...
 *
 */

/*
 * Contributor(s): Armando Stellato stellato@info.uniroma2.it
 */
package it.uniroma2.art.owlart.protegeimpl.models;

import static it.uniroma2.art.owlart.protegeimpl.TripleType.*;
import it.uniroma2.art.owlart.exceptions.ModelAccessException;
import it.uniroma2.art.owlart.exceptions.ModelUpdateException;
import it.uniroma2.art.owlart.exceptions.UnsupportedQueryLanguageException;
import it.uniroma2.art.owlart.exceptions.UnsupportedRDFFormatException;
import it.uniroma2.art.owlart.io.RDFFormat;
import it.uniroma2.art.owlart.model.ARTBNode;
import it.uniroma2.art.owlart.model.ARTLiteral;
import it.uniroma2.art.owlart.model.ARTNode;
import it.uniroma2.art.owlart.model.ARTResource;
import it.uniroma2.art.owlart.model.ARTStatement;
import it.uniroma2.art.owlart.model.ARTURIResource;
import it.uniroma2.art.owlart.model.NodeFilters;
import it.uniroma2.art.owlart.models.BaseRDFTripleModel;
import it.uniroma2.art.owlart.navigation.ARTNamespaceIterator;
import it.uniroma2.art.owlart.navigation.ARTNodeIterator;
import it.uniroma2.art.owlart.navigation.ARTResourceIterator;
import it.uniroma2.art.owlart.navigation.ARTStatementIterator;
import it.uniroma2.art.owlart.navigation.ARTURIResourceIterator;
import it.uniroma2.art.owlart.protegeimpl.Protege2ARTResourceFactory;
import it.uniroma2.art.owlart.protegeimpl.ProtegeARTNamespaceIteratorImpl;
import it.uniroma2.art.owlart.protegeimpl.ProtegeARTNodeIteratorImpl;
import it.uniroma2.art.owlart.protegeimpl.ProtegeARTResourceIteratorImpl;
import it.uniroma2.art.owlart.protegeimpl.ProtegeARTStatementIteratorImpl;
import it.uniroma2.art.owlart.protegeimpl.TripleType;
import it.uniroma2.art.owlart.protegeimpl.io.RDFFormatConverter;
import it.uniroma2.art.owlart.protegeimpl.model.ARTURIResourceEmptyImpl;
import it.uniroma2.art.owlart.query.BooleanQuery;
import it.uniroma2.art.owlart.query.GraphQuery;
import it.uniroma2.art.owlart.query.MalformedQueryException;
import it.uniroma2.art.owlart.query.Query;
import it.uniroma2.art.owlart.query.QueryLanguage;
import it.uniroma2.art.owlart.query.TupleQuery;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import edu.stanford.smi.protege.model.Project;
import edu.stanford.smi.protegex.owl.model.OWLModel;
import edu.stanford.smi.protegex.owl.model.RDFObject;
import edu.stanford.smi.protegex.owl.model.RDFProperty;
import edu.stanford.smi.protegex.owl.model.RDFResource;
import edu.stanford.smi.protegex.owl.model.triplestore.Triple;
import edu.stanford.smi.protegex.owl.model.triplestore.TripleStore;
import edu.stanford.smi.protegex.owl.model.triplestore.TripleStoreModel;

public class BaseRDFModelProtegeImpl implements BaseRDFTripleModel {

	protected static Logger logger = LoggerFactory.getLogger(BaseRDFModelProtegeImpl.class);

	public static final String graphUnsupportedMsg = "named graphs are not supported in Protege Implementatin of OWL Art API";

	protected Project project;
	protected OWLModel owlModel;
	protected TripleStore tripleStore;
	protected TripleStoreModel tripleStoreModel;
	protected String baseURI;
	protected String defaultNamespace;

	public BaseRDFModelProtegeImpl(Project project) {
		this.project = project;
		owlModel = (OWLModel) project.getKnowledgeBase();
		tripleStoreModel = owlModel.getTripleStoreModel();
		tripleStore = tripleStoreModel.getTopTripleStore();
	}

	public Project getProtegeProject() {
		return project;
	}

	public OWLModel getProtegeOWLModel() {
		return owlModel;
	}

	TripleStore getTripleStore() {
		return tripleStore;
	}

	TripleStoreModel getTripleStoreModel() {
		return tripleStoreModel;
	}

	public void addRDF(File inputFile, String baseURI, RDFFormat rdfFormat, ARTResource... graphs)
			throws FileNotFoundException, IOException, ModelAccessException, ModelUpdateException,
			UnsupportedRDFFormatException {
		throw new IllegalAccessError("import still not implemented in Protege implementation of OWL ART API");

	}

	public void addRDF(URL url, String baseURI, RDFFormat rdfFormat, ARTResource... graphs)
			throws FileNotFoundException, IOException, ModelAccessException, ModelUpdateException,
			UnsupportedRDFFormatException {
		throw new IllegalAccessError("import still not implemented in Protege implementation of OWL ART API");

	}

	public void addStatement(ARTStatement stat, ARTResource... graphs) throws ModelUpdateException {
		if (graphs != null && graphs.length != 0)
			throw new IllegalAccessError(graphUnsupportedMsg);
		tripleStore.add(Protege2ARTResourceFactory.aRTStatement2ProtegeTriple(stat));
		// TODO Auto-generated method stub

	}

	public void addTriple(ARTResource subject, ARTURIResource predicate, ARTNode object,
			ARTResource... graphs) throws ModelUpdateException {
		if (graphs != null && graphs.length != 0)
			throw new IllegalAccessError(graphUnsupportedMsg);
		/*
		 * tripleStore.add(Protege2ARTResourceFactory.aRTResource2ProtegeResource(subject),
		 * Protege2ARTResourceFactory.aRTResource2ProtegeProperty(predicate), Protege2ARTResourceFactory
		 * .aRTNode2ProtegeValue(object));
		 */
		tripleStore.add(

		Protege2ARTResourceFactory.aRTResource2ProtegeResource(subject, owlModel),

		Protege2ARTResourceFactory.aRTURIResource2ProtegeProperty(predicate, owlModel),

		Protege2ARTResourceFactory.aRTNode2ProtegeValue(object, owlModel));

	}

	public ARTURIResource createURIResource(String uri) {

		return new ARTURIResourceEmptyImpl(uri, owlModel);

		/*
		 * RDFResource resource=null; resource = owlModel.getRDFResource(uri); if (resource==null) {
		 * //logger.debug("resource does not exist"); resource = owlModel.createRDFUntypedResource(uri); }
		 * //else {logger.debug("resource does exist");} return
		 * Protege2ARTResourceFactory.protegeResource2ARTURIResource(resource);
		 */
	}

	public void close() throws ModelUpdateException {
		project.dispose();
	}

	public ARTBNode createBNode() {
		throw new IllegalStateException("still to be implemented");
	}

	public ARTBNode createBNode(String ID) {
		throw new IllegalStateException("still to be implemented");
	}

	public ARTLiteral createLiteral(String literalString, ARTURIResource datatype) {
		return Protege2ARTResourceFactory.protegeLiteral2ARTLiteral(owlModel.createRDFSLiteral(literalString,
				Protege2ARTResourceFactory.aRTURIResource2ProtegeRDFSDatatype(datatype, owlModel)));
	}

	public ARTLiteral createLiteral(String literalString) {
		return Protege2ARTResourceFactory.protegeLiteral2ARTLiteral(owlModel.createRDFSLiteral(literalString,
				(String) null));
	}

	public ARTLiteral createLiteral(String literalString, String language) {
		return Protege2ARTResourceFactory.protegeLiteral2ARTLiteral(owlModel.createRDFSLiteral(literalString,
				language));
	}

	public void deleteStatement(ARTStatement statement, ARTResource... graphs) throws ModelUpdateException {
		if (graphs != null && graphs.length != 0)
			throw new IllegalAccessError(graphUnsupportedMsg);
		tripleStore.remove(Protege2ARTResourceFactory.aRTStatement2ProtegeTriple(statement));
	}

	public void deleteTriple(ARTResource subject, ARTURIResource property, ARTNode object,
			ARTResource... graphs) throws ModelUpdateException {
		if (graphs != null && graphs.length != 0)
			throw new IllegalAccessError(graphUnsupportedMsg);
		tripleStore.remove(Protege2ARTResourceFactory.aRTResource2ProtegeResource(subject, owlModel),
				Protege2ARTResourceFactory.aRTURIResource2ProtegeProperty(property, owlModel),
				Protege2ARTResourceFactory.aRTNode2ProtegeValue(object, owlModel));
	}

	public String getBaseURI() {
		return baseURI;
	}

	public String getDefaultNamespace() {
		return tripleStore.getDefaultNamespace();
	}

	public boolean hasStatement(ARTStatement stat, boolean inferred, ARTResource... graphs)
			throws ModelAccessException {
		if (graphs != null && graphs.length != 0)
			throw new IllegalAccessError(graphUnsupportedMsg);
		return tripleStore.contains(Protege2ARTResourceFactory.aRTStatement2ProtegeTriple(stat));
	}

	public boolean hasTriple(ARTResource subj, ARTURIResource pred, ARTNode obj, boolean inferred,
			ARTResource... graphs) throws ModelAccessException {
		if (graphs != null && graphs.length != 0)
			throw new IllegalAccessError(graphUnsupportedMsg);
		return tripleStore.contains(Protege2ARTResourceFactory.aRTResource2ProtegeResource(subj, owlModel),
				Protege2ARTResourceFactory.aRTURIResource2ProtegeProperty(pred, owlModel),
				Protege2ARTResourceFactory.aRTNode2ProtegeValue(obj, owlModel));
	}

	
	
	@SuppressWarnings("unchecked")
	public ARTResourceIterator listSubjectsOfPredObjPair(ARTURIResource predicate, ARTNode object,
			boolean inferred, ARTResource... graphs) throws ModelAccessException {
		if (graphs != null && graphs.length != 0)
			throw new IllegalAccessError(BaseRDFModelProtegeImpl.graphUnsupportedMsg);

		logger.debug("predicate: "
				+ Protege2ARTResourceFactory.aRTURIResource2ProtegeProperty(predicate, owlModel));
		logger.debug("value: " + Protege2ARTResourceFactory.aRTNode2ProtegeValue(object, owlModel));

		Iterator<RDFResource> it = tripleStore.listSubjects(Protege2ARTResourceFactory
				.aRTURIResource2ProtegeProperty(predicate, owlModel), Protege2ARTResourceFactory
				.aRTNode2ProtegeValue(object, owlModel));
		return new ProtegeARTResourceIteratorImpl(it);
	}

	@SuppressWarnings("unchecked")
	public ARTNodeIterator listValuesOfSubjPredPair(ARTResource subject, ARTURIResource predicate,
			boolean inferred, ARTResource... graphs) throws ModelAccessException {
		if (graphs != null && graphs.length != 0)
			throw new IllegalAccessError(BaseRDFModelProtegeImpl.graphUnsupportedMsg);
		Iterator<RDFObject> it = tripleStore.listObjects(Protege2ARTResourceFactory
				.aRTResource2ProtegeResource(subject, owlModel), Protege2ARTResourceFactory
				.aRTURIResource2ProtegeProperty(predicate, owlModel));
		return new ProtegeARTNodeIteratorImpl(it);
	}
	
	public ARTURIResourceIterator listPredicatesOfSubjObjPair(ARTResource subject, ARTNode object,
			boolean inferred, ARTResource... graphs) throws ModelAccessException {
		throw new IllegalAccessError(
				"in Protege implementation we're unable to list predicates binding provided subject and object; sorry no alternative solution");		
	}
	
	public ARTResourceIterator listNamedGraphs() throws ModelAccessException {
		throw new IllegalAccessError(graphUnsupportedMsg);
	}

	@SuppressWarnings("unchecked")
	public ARTNamespaceIterator listNamespaces() throws ModelAccessException {
		ArrayList<String> namespaces = new ArrayList<String>();
		Collection<String> prefixes = tripleStore.getPrefixes();
		for (String prefix : prefixes)
			namespaces.add(tripleStore.getNamespaceForPrefix(prefix));
		return new ProtegeARTNamespaceIteratorImpl(namespaces.iterator(), tripleStore);
	}

	public ARTStatementIterator listStatements(ARTResource subj, ARTURIResource pred, ARTNode obj,
			boolean inferred, ARTResource... graphs) throws ModelAccessException {
		if (graphs != null && graphs.length != 0)
			throw new IllegalAccessError(graphUnsupportedMsg);

		Iterator<Triple> tripleIterator = null;

		RDFResource subject = null;
		RDFProperty predicate = null;
		RDFObject object = null;

		if (subj != NodeFilters.ANY)
			subject = Protege2ARTResourceFactory.aRTResource2ProtegeResource(subj, owlModel);
		if (pred != NodeFilters.ANY)
			predicate = Protege2ARTResourceFactory.aRTURIResource2ProtegeProperty(pred, owlModel);
		if (obj != NodeFilters.ANY)
			object = Protege2ARTResourceFactory.aRTNode2ProtegeValue(obj, owlModel);

		TripleType type = getTripleType(subj, pred, obj);

		switch (type) {
		case AAA:
			logger.debug("case AAA");
			tripleIterator = tripleStore.listTriples();
			break;
		case AAO:
			logger.debug("case AAO");
			tripleIterator = tripleStore.listTriplesWithObject(object);
			break;
		case SAA:
			logger.debug("case SAA; subject = " + subject);
			tripleIterator = tripleStore.listTriplesWithSubject(subject);
			/*
			 * while (tripleIterator.hasNext()) logger.debug("" + tripleIterator.next());
			 */
			break;
		case APA:
			logger.debug("type: " + APA);
			logger.error("in Protege implementation we're unable to list triples through specification of the sole predicate; sorry no alternative solution");
			throw new IllegalAccessError(
					"in Protege implementation we're unable to list triples through specification of the sole predicate; sorry no alternative solution");
		case APO: {
			logger.debug("type: " + APO + "for predicate: " + predicate + " and object: " + object);
			logger.error("in Protege implementation we're unable to list triples through specification of predicateobject pairs; hint: use listSubjectsOfPredObjPair... instead");
			throw new IllegalAccessError(
					"in Protege implementation we're unable to list triples through specification of predicateobject pairs; hint: use listSubjectsOfPredObjPair... instead");
		}
		case SAO:
			logger.debug("type: " + SAO);
			logger.error("in Protege implementation we're unable to list triples through specification of subjectobject pairs; hint: use RDF.listPredicates... instead");
			throw new IllegalAccessError(
					"in Protege implementation we're unable to list triples through specification of subjectobject pairs; hint: use RDF.listPredicates... instead");
		case SPA:
			logger.debug("type: " + SPA);
			logger.error("in Protege implementation we're unable to list triples through specification of subjectpredicate pairs; hint: use RDF.listValuesOfSubjPredPair... instead");
			throw new IllegalAccessError(
					"in Protege implementation we're unable to list triples through specification of subjectpredicate pairs; hint: use RDF.listValuesOfSubjPredPair... instead");
		case SPO: {
			ArrayList<Triple> tripleList = new ArrayList<Triple>();
			if (tripleStore.contains(subject, predicate, object))
				tripleList.add(owlModel.createTriple(subject, predicate, object));
			tripleIterator = tripleList.iterator();
		}
		}

		return new ProtegeARTStatementIteratorImpl(tripleIterator);
	}

	public void setBaseURI(String uri) throws ModelUpdateException {
		baseURI = uri;

	}

	public void setDefaultNamespace(String namespace) throws ModelUpdateException {
		tripleStore.setDefaultNamespace(namespace);
	}

	public void writeRDF(File outputFile, RDFFormat rdfFormat, ARTResource... graphs) throws IOException,
			ModelAccessException, UnsupportedRDFFormatException {
		if (graphs != null && graphs.length != 0)
			throw new IllegalAccessError(graphUnsupportedMsg);
		FileOutputStream fos = new FileOutputStream(outputFile);
		owlModel.getJenaModel().write(fos, RDFFormatConverter.convert(rdfFormat), baseURI);

	}

	public void writeRDF(OutputStream os, RDFFormat rdfFormat, ARTResource... graphs) throws IOException,
			ModelAccessException, UnsupportedRDFFormatException {
		if (graphs != null && graphs.length != 0)
			throw new IllegalAccessError(graphUnsupportedMsg);
		owlModel.getJenaModel().write(os, RDFFormatConverter.convert(rdfFormat), baseURI);

	}

	public String expandQName(String qname) throws ModelAccessException {
		return owlModel.getURIForResourceName(qname);
	}

	public String getNSForPrefix(String prefix) throws ModelAccessException {
		return tripleStore.getNamespaceForPrefix(prefix);
	}

	@SuppressWarnings("unchecked")
	public Map<String, String> getNamespacePrefixMapping() throws ModelAccessException {
		Map<String, String> res = new HashMap<String, String>();
		Collection<String> prefixes = tripleStore.getPrefixes();
		for (String prefix : prefixes) {
			res.put(prefix, tripleStore.getNamespaceForPrefix(prefix));
		}
		return res;
	}

	public String getPrefixForNS(String namespace) throws ModelAccessException {
		return tripleStore.getPrefix(namespace);
	}

	public String getQName(String uri) throws ModelAccessException {
		return owlModel.getResourceNameForURI(uri);
	}

	// TODO Auto-generated method stub remve this from the API!!!
	public boolean hasExplicitPrefixMapping(String namespace) throws ModelAccessException {
		return false;
	}

	public void removeNsPrefixMapping(String namespace) throws ModelUpdateException {
		tripleStore.removePrefix(tripleStore.getPrefix(namespace));

	}

	public void setNsPrefix(String namespace, String prefix) throws ModelUpdateException {
		tripleStore.setPrefix(namespace, prefix);

	}

	public TripleType getTripleType(ARTResource subj, ARTURIResource pred, ARTNode obj) {
		if (subj == NodeFilters.ANY) {
			if (pred == NodeFilters.ANY) {
				if (obj == NodeFilters.ANY)
					return AAA;
				else
					return AAO;
			} else {
				if (obj == NodeFilters.ANY)
					return APA;
				else
					return APO;
			}
		} else {
			if (pred == NodeFilters.ANY) {
				if (obj == NodeFilters.ANY)
					return SAA;
				else
					return SAO;
			} else {
				if (obj == NodeFilters.ANY)
					return SPA;
				else
					return SPO;
			}
		}
	}

	public final String SPARQLnotSupported = "SPARQL nor any other query language is supported at the moment in Protege Impl of OWLArt API";

	public BooleanQuery createBooleanQuery(QueryLanguage ql, String query, String baseURI)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		throw new UnsupportedQueryLanguageException(SPARQLnotSupported);
	}

	public GraphQuery createGraphQuery(QueryLanguage ql, String query, String baseURI)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		throw new UnsupportedQueryLanguageException(SPARQLnotSupported);
	}

	public Query createQuery(QueryLanguage ql, String query, String baseURI)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		throw new UnsupportedQueryLanguageException(SPARQLnotSupported);
	}

	public TupleQuery createTupleQuery(QueryLanguage ql, String query, String baseURI)
			throws UnsupportedQueryLanguageException, ModelAccessException, MalformedQueryException {
		throw new UnsupportedQueryLanguageException(SPARQLnotSupported);
	}

	public void clearRDF(ARTResource... graphs) throws ModelUpdateException {
		throw new IllegalAccessError("RDF clearance is not supported by this repository ");
	}



	/*
	 * *************************************** Local Utility Methods **************************************
	 */
	/*
	 * private Resource[] getSesameContexts(ARTResource... namedGraphs) { return getSesameContexts(false,
	 * namedGraphs); }
	 * 
	 * private Resource[] getSesameContexts(boolean add, ARTResource... graphs) { if (graphs == null) throw
	 * new IllegalArgumentException(); Resource[] resContexts = new Resource[graphs.length]; for (int i = 0; i
	 * < graphs.length; i++) { if (graphs[i] == null) throw new IllegalArgumentException(); // a null value
	 * for a contest is not accepted in OWLArt // (though in Sesame it means: null-context), null-context
	 * (main unnamed graph) in OWLArt must be // specified as MAINGRAPH else if (graphs[i] == NodeFilters.ANY)
	 * // if any of the graphs is ANY, then an empty array is sent // to // Sesame (which interpretes as all
	 * graphs when reading/deleting and maingraph when adding // triples) // TODO could be implemented, when
	 * writing, as a special NodeFilter/graphs which always fails // the equals if (!add) return new
	 * Resource[0]; else throw new IllegalArgumentException(
	 * "the ANY Node can be used only when reading/deleting triples from the model, not for write"); else if
	 * (graphs[i] == NodeFilters.MAINGRAPH) resContexts[i] = null; // in Sesame, MAINGRAPH is implemented with
	 * null on the graphs else resContexts[i] =
	 * Sesame2ARTResourceFactory.aRTResource2SesameResource(graphs[i]); } return resContexts; }
	 */
}
